package com.simplecode.arcgistools.utils.coordinate;

import com.simplecode.arcgistools.utils.coordinate.baidu.Coordinate;
import com.simplecode.arcgistools.utils.coordinate.baidu.CoordinateTransformUtil;
import com.simplecode.arcgistools.utils.coordinate.enums.EpsgConverterEnum;
import lombok.extern.slf4j.Slf4j;
import org.locationtech.proj4j.*;

/**
 * @author liuxy
 * @version 1.0
 * @Project: ArcGis-Tools
 * @DateTime: 2023/5/30 11:38
 * @Description: 坐标转换器工具类
 * @since JDK 1.8
 */
@Slf4j
public class CoordinateConverter {

    /**
     * 原始坐标信息
     */
    private CoordinateReferenceSystem sourceCoordinate;

    /**
     * 转换坐标信息
     */
    private CoordinateReferenceSystem targetCoordinate;

    /**
     * 转换器的转换坐标系源配置
     */
    private CoordinateTransform transform;

    /**
     * CRS工程配置
     */
    private CRSFactory crsFactory;

    /**
     * Epsg 转换枚举信息
     */
    private EpsgConverterEnum epsgConverterEnum;

    /**
     * 构造器
     *
     * @return
     */
    public static CoordinateConverter Builder() {
        return new CoordinateConverter();
    }

    /**
     * 转换器枚举 配置
     *
     * @param epsgConverterEnum epsg 转换枚举信息
     * @return 当前转换构造器
     */
    public CoordinateConverter transform(EpsgConverterEnum epsgConverterEnum) {
        this.epsgConverterEnum = epsgConverterEnum;
        return this;
    }

    /**
     * EPSG 坐标转换器
     *
     * @param coordinate 经纬度数据
     * @return 转换结果
     */
    private Coordinate epsgConvert(Coordinate coordinate) {
        // 初始化 CRS对象
        crsFactory = new CRSFactory();
        // 创建要转换的对象
        sourceCoordinate = crsFactory.createFromName(epsgConverterEnum.getSourceEpsgEnum().getCode());
        // 创建转后后的对象
        targetCoordinate = crsFactory.createFromName(epsgConverterEnum.getTargetEpsgEnum().getCode());
        // 构建转换前的对象
        ProjCoordinate srcCoord = new ProjCoordinate(coordinate.getLongitude(), coordinate.getLatitude());
        // 构建转换后的对象
        ProjCoordinate targetCoord = new ProjCoordinate();
        // 初始化转换器
        transform = new BasicCoordinateTransform(sourceCoordinate, targetCoordinate);
        // 开始转换
        transform.transform(srcCoord, targetCoord);
        // 转换结果
        return Coordinate.builder().longitude(targetCoord.x).latitude(targetCoord.y).build();
    }


    /**
     * 转换构造器
     *
     * @param coordinate 转换数据对象
     * @return 转换结果数据
     */
    public Coordinate convert(Coordinate coordinate) {
        log.info("");
        log.info("===========开始转换坐标===========");
        log.info("=== 原始 坐标系：【{}】", epsgConverterEnum.getSourceEpsgEnum().getName());
        log.info("=== 目标 坐标系：【{}】", epsgConverterEnum.getTargetEpsgEnum().getName());
        // 转换器选择
        Coordinate coordinateResult = switch (epsgConverterEnum) {
            // 北京54 坐标系 转 GCJ-02 火星坐标系
            case BEIJING54_TO_GCJ02_COORDINATE ->
                    CoordinateTransformUtil.wgs84ToGcj02(CoordinateConverter.Builder().transform(EpsgConverterEnum.BEIJING54_TO_WGS84_COORDINATE).convert(coordinate));
            // 北京54 坐标系 转 BD09 百度坐标系
            case BEIJING54_TO_BD09_COORDINATE ->
                    CoordinateTransformUtil.gcj02ToBd09(CoordinateTransformUtil.wgs84ToGcj02(CoordinateConverter.Builder().transform(EpsgConverterEnum.BEIJING54_TO_WGS84_COORDINATE).convert(coordinate)));

            // 西安80 坐标系 转 GCJ-02 火星坐标系
            case XIAN80_TO_GCJ02_COORDINATE ->
                    CoordinateTransformUtil.wgs84ToGcj02(CoordinateConverter.Builder().transform(EpsgConverterEnum.XIAN80_TO_WGS84_COORDINATE).convert(coordinate));
            // 西安80 坐标系 转 BD09 百度坐标系
            case XIAN80_TO_BD09_COORDINATE ->
                    CoordinateTransformUtil.gcj02ToBd09(CoordinateTransformUtil.wgs84ToGcj02(CoordinateConverter.Builder().transform(EpsgConverterEnum.XIAN80_TO_WGS84_COORDINATE).convert(coordinate)));

            // WGS84 坐标系 转 GCJ-02 火星坐标系
            case WGS84_TO_GCJ02_COORDINATE -> CoordinateTransformUtil.wgs84ToGcj02(coordinate);
            // WGS84 坐标系 转 BD09 百度坐标系
            case WGS84_TO_BD09_COORDINATE ->
                    CoordinateTransformUtil.gcj02ToBd09(CoordinateTransformUtil.wgs84ToGcj02(coordinate));

            // 国家CGCS2000 坐标系 转 GCJ-02 火星坐标系
            case CGCS2000_TO_GCJ02_COORDINATE ->
                    CoordinateTransformUtil.wgs84ToGcj02(CoordinateConverter.Builder().transform(EpsgConverterEnum.CGCS2000_TO_WGS84_COORDINATE).convert(coordinate));
            // 国家CGCS2000 坐标系 转 BD09 百度坐标系
            case CGCS2000_TO_BD09_COORDINATE ->
                    CoordinateTransformUtil.gcj02ToBd09(CoordinateTransformUtil.wgs84ToGcj02(CoordinateConverter.Builder().transform(EpsgConverterEnum.CGCS2000_TO_WGS84_COORDINATE).convert(coordinate)));

            // GCJ-02 火星坐标系 转 北京54 坐标系
            case GCJ02_TO_BEIJING54_COORDINATE ->
                    CoordinateConverter.Builder().transform(EpsgConverterEnum.WGS84_TO_BEIJING54_COORDINATE).convert(CoordinateTransformUtil.gcj02ToWgs84(coordinate));
            // GCJ-02 火星坐标系 转 西安80 坐标系
            case GCJ02_TO_XIAN80_COORDINATE ->
                    CoordinateConverter.Builder().transform(EpsgConverterEnum.WGS84_TO_XIAN80_COORDINATE).convert(CoordinateTransformUtil.gcj02ToWgs84(coordinate));
            // GCJ-02 火星坐标系 转 WGS84 坐标系
            case GCJ02_TO_WGS84_COORDINATE -> CoordinateTransformUtil.gcj02ToWgs84(coordinate);
            // GCJ-02 火星坐标系 转 国家CGCS2000 坐标系
            case GCJ02_TO_CGCS2000_COORDINATE ->
                    CoordinateConverter.Builder().transform(EpsgConverterEnum.WGS84_TO_CGCS2000_COORDINATE).convert(CoordinateTransformUtil.gcj02ToWgs84(coordinate));
            // GCJ-02 火星坐标系 转 BD09 百度坐标
            case GCJ02_TO_BD09_COORDINATE -> CoordinateTransformUtil.gcj02ToBd09(coordinate);
            // GCJ-02 火星坐标系 转 WebMercator 投影坐标系
            case GCJ02_TO_WEB_MERCATOR_COORDINATE ->
                    CoordinateConverter.Builder().transform(EpsgConverterEnum.WGS84_TO_WEB_MERCATOR_COORDINATE).convert(CoordinateTransformUtil.gcj02ToWgs84(coordinate));

            // BD09百度坐标系 转 北京54 坐标系
            case BD09_TO_BEIJING54_COORDINATE ->
                    CoordinateConverter.Builder().transform(EpsgConverterEnum.WGS84_TO_BEIJING54_COORDINATE).convert(CoordinateTransformUtil.bd09ToWgs84(coordinate));
            // BD09百度坐标系 转 西安80 坐标系
            case BD09_TO_XIAN80_COORDINATE ->
                    CoordinateConverter.Builder().transform(EpsgConverterEnum.WGS84_TO_XIAN80_COORDINATE).convert(CoordinateTransformUtil.bd09ToWgs84(coordinate));
            // BD09百度坐标系 转 WGS84 坐标系
            case BD09_TO_WGS84_COORDINATE -> CoordinateTransformUtil.bd09ToWgs84(coordinate);
            // BD09百度坐标系 转 国家CGCS2000 坐标系
            case BD09_TO_CGCS2000_COORDINATE ->
                    CoordinateConverter.Builder().transform(EpsgConverterEnum.WGS84_TO_CGCS2000_COORDINATE).convert(CoordinateTransformUtil.bd09ToWgs84(coordinate));
            // BD09百度坐标系 转 GCJ-02 火星坐标系
            case BD09_TO_GCJ02_COORDINATE -> CoordinateTransformUtil.bd09ToGcj02(coordinate);
            // BD09百度坐标系 转 WebMercator 投影坐标系
            case BD09_TO_WEB_MERCATOR_COORDINATE ->
                    CoordinateConverter.Builder().transform(EpsgConverterEnum.WGS84_TO_WEB_MERCATOR_COORDINATE).convert(CoordinateTransformUtil.bd09ToWgs84(coordinate));

            // WebMercator 投影坐标系 转 GCJ-02 火星坐标系
            case WEB_MERCATOR_TO_GCJ02_COORDINATE ->
                    CoordinateTransformUtil.wgs84ToGcj02(CoordinateConverter.Builder().transform(EpsgConverterEnum.WEB_MERCATOR_TO_WGS84_COORDINATE).convert(coordinate));
            // WebMercator 投影坐标系 转 BD09 百度坐标系
            case WEB_MERCATOR_TO_BD09_COORDINATE ->
                    CoordinateTransformUtil.gcj02ToBd09(CoordinateTransformUtil.wgs84ToGcj02(CoordinateConverter.Builder().transform(EpsgConverterEnum.WEB_MERCATOR_TO_WGS84_COORDINATE).convert(coordinate)));
            // 默认走 epsg 转换器
            default -> epsgConvert(coordinate);
        };
        log.info("=== 转换前 经纬度坐标: {},{}", coordinate.getLongitude(), coordinate.getLatitude());
        log.info("=== 转换后 经纬度坐标: {},{}", coordinateResult.getLongitude(), coordinateResult.getLatitude());
        log.info("===========完成转换坐标=========");
        log.info("");
        return coordinateResult;
    }

    /**
     * 转换构造器
     *
     * @param latitude  纬度
     * @param longitude 经度
     * @return 转换结果数据
     */

    public double[] convertDouble(double longitude, double latitude) {
        Coordinate convert = this.convert(new Coordinate(latitude, longitude));
        return new double[]{convert.getLongitude(), convert.getLatitude()};
    }

    /**
     * 转换构造器
     *
     * @param longitude 经度
     * @param latitude  纬度
     * @return 转换结果数据
     */
    public Coordinate convertCoordinate(double longitude, double latitude) {
        return this.convert(new Coordinate(latitude, longitude));
    }

    /**
     * 是否在中国境外
     * Is it outside of China.
     *
     * @param longitude 经度
     * @param latitude 纬度
     * @return true/false
     */
    public boolean outOfChina(double longitude, double latitude) {
        return outOfChina(new Coordinate(longitude, latitude));

    }

    /**
     * 是否在中国境外
     * Is it outside of China.
     *
     * @param coordinate 经纬度数据对象
     * @return true/false
     */
    public boolean outOfChina(Coordinate coordinate) {
        if (coordinate.getLongitude() < 72.004 || coordinate.getLongitude() > 137.8347) {
            return true;
        }
        if (coordinate.getLatitude() < 0.8293 || coordinate.getLatitude() > 55.8271) {
            return true;
        }
        return false;
    }

}
